/*
 * Copyright 2010 John Kozura
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.bfr.client.selection;

import com.bfr.client.selection.impl.*;

import com.google.gwt.core.client.*;
import com.google.gwt.dom.client.Document;

/**
* A selection within a particular document.  Holds the singleton for a
* particlar document/window for getting and setting the selection.
* 
* @author John Kozura
*/
public final class Selection
{
    public static final String START_NODE	= "startContainer";
    public static final String START_OFFSET	= "startOffset";
    public static final String END_NODE		= "endContainer";
    public static final String END_OFFSET	= "endOffset";
    public static final String IS_COLLAPSED 	= "isCollapsed";    
    
    /**
    * Clears or removes any current text selection.
    */
    public static void clearAnySelectedText() 
    {
	getSelection().clear();
    }
    /**
    * Convenience for getting the range for the current browser selection
    *
    * @return A range object representing the browser window's selection
    */
    public static Range getBrowserRange()
    {
	return getSelection().getRange();
    }
    
    /**
    * Returns the selection for a given window, for instance an iframe
    * 
    * @return The singleton instance
    */
    static public Selection getSelection(JavaScriptObject window) 
    {
	SelectionImpl.JSSel jsSel = c_impl.getSelection(window);
	Selection res = new Selection();
	res.m_selection = jsSel;
	res.m_document = getDocument(window);
	return res;
    }
    
    public static native Document getDocument(JavaScriptObject window)
    /*-{
	return window.document;
    }-*/;

    private SelectionImpl.JSSel m_selection;
    
    private Document m_document;
    
    /**
    * The browser aware support that takes care of browser difference nasties.
    */
    private static SelectionImpl c_impl = 
	(SelectionImpl)GWT.create(SelectionImpl.class);
    
    /**
    * Returns the document Selection singleton
    * 
    * @return The singleton instance
    */
    static public Selection getSelection() 
    {
	return Selection.getSelection(getWindow());
    }
    
    static SelectionImpl getImpl()
    {
	return Selection.c_impl;
    }
    
    static native private JavaScriptObject getWindow()
    /*-{
	return $wnd;
    }-*/;
    
    protected Selection()
    {
	super();
    }
    
    /**
    * Clears any current selection.
    */
    public void clear()
    {
	Selection.getImpl().clear(m_selection);
    }
    
    /**
    * Gets the parent document associated with this selection.  Could be
    * different from the browser document if, for example this is the selection
    * within an iframe.
    *
    * @return parent document of this selection
    */
    public Document getDocument() {return m_document;}
    
    /**
    * Get the javascript object representing the selection.  Since this is
    * browser dependent object, should probably not use this.
    *
    * @return a JavaScriptObject representing this selection
    */
    public SelectionImpl.JSSel getJSSelection() {return m_selection;}
    
    /**
    * Gets the range associated with the given selection.  The endpoints are
    * captured immediately, so any changes to the selection will not affect
    * the returned range.  In some browsers (IE) this can return NULL if
    * nothing is selected in the document.
    *
    * @return A range object capturing the current selection
    */
    public Range getRange()
    {
	Range res = null;
	RangeImpl.JSRange jsRange = c_impl.getJSRange(m_document, m_selection);
	if (jsRange != null)
	{
	    res = new Range(m_document, jsRange);
	    res.ensureEndPoints();
	}
	return res;
    }
    
    /**
    * Tests if anything is currently being selected
    * 
    * @return True if empty false otherwise
    */
    public boolean isEmpty()
    {
	return Selection.getImpl().isEmpty(m_selection);
    }
    
    /**
    * Takes a range object and pushes it to be the new selection.  The range
    * must be parented by the same window/document as the selection.  The range
    * remains separate from the selection after this operation; any changes to
    * the range are not reflected in the selection, and vice versa.
    *
    * @param newSelection What the selection should be
    */
    public void setRange(Range newSelection)
    {
	if (newSelection.getDocument() == m_document)
	{
	    c_impl.setJSRange(m_selection, newSelection.getJSRange());
	}
    }
}
